package gek.uni.diplom.ocl.example.visitors;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.ocl.expressions.ExpressionsFactory;
import org.eclipse.ocl.expressions.IntegerLiteralExp;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.PropertyCallExp;
import org.eclipse.ocl.utilities.AbstractVisitor;
import org.eclipse.ocl.utilities.PredefinedType;

public class MyOclVisitor<C, O, P, EL, PM, S, COA, SSA, CT>
        extends
        AbstractVisitor<Map<String, List<Object>>, C, O, P, EL, PM, S, COA, SSA, CT> {

    public static String LEFT_SIDE_KEY = "left";
    public static String RIGHT_SIDE_KEY = "right";

    @Override
    public Map<String, List<Object>> visitOperationCallExp(
            OperationCallExp<C, O> callExp) {
        int operationCode = callExp.getOperationCode();
        switch (operationCode) {
        case PredefinedType.PLUS:
            System.out.println();
            break;
        default:
            break;
        }
        return super.visitOperationCallExp(callExp);
    }

    @Override
    protected Map<String, List<Object>> handleOperationCallExp(
            OperationCallExp<C, O> callExp,
            Map<String, List<Object>> sourceResult,
            List<Map<String, List<Object>>> argumentResults) {

        int opCode = callExp.getOperationCode();
        switch (opCode) {
        case PredefinedType.EQUAL:
        case PredefinedType.NOT_EQUAL:
        case PredefinedType.LESS_THAN:
        case PredefinedType.LESS_THAN_EQUAL:
        case PredefinedType.GREATER_THAN:
        case PredefinedType.GREATER_THAN_EQUAL:
            return createOrderResult(callExp, sourceResult, argumentResults);
            // break;

        case PredefinedType.PLUS:
        case PredefinedType.MINUS:
            return createStandartResult(callExp, sourceResult, argumentResults);
            // break;

        default:
            return super.handleOperationCallExp(callExp, sourceResult,
                    argumentResults);
        }

    }

    @Override
    protected Map<String, List<Object>> handlePropertyCallExp(
            PropertyCallExp<C, P> callExp,
            Map<String, List<Object>> sourceResult,
            List<Map<String, List<Object>>> qualifierResults) {

        Map<String, List<Object>> res = new HashMap<String, List<Object>>();
        List<Object> lst = new ArrayList<Object>();
        lst.add(callExp);

        res.put(LEFT_SIDE_KEY, lst);

        return res;
    }

    @Override
    public Map<String, List<Object>> visitIntegerLiteralExp(
            IntegerLiteralExp<C> literalExp) {

        Map<String, List<Object>> res = new HashMap<String, List<Object>>();
        List<Object> lst = new ArrayList<Object>();
        lst.add(literalExp);
        res.put(RIGHT_SIDE_KEY, lst);
        return res;
    }

    /**
     * 
     * создает ocl-выражение на основе переданных параметров.
     * 
     * @param sourceResult
     * @param argumentResults
     * @return
     */
    private Map<String, List<Object>> createOrderResult(
            OperationCallExp<C, O> callExp,
            Map<String, List<Object>> sourceResult,
            List<Map<String, List<Object>>> argumentResults) {
        
        OperationCallExp<C, O> newCallExp = ExpressionsFactory.eINSTANCE.createOperationCallExp();
        newCallExp.setName(callExp.getName());
        newCallExp.setOperationCode(callExp.getOperationCode());
        newCallExp.setReferredOperation(callExp.getReferredOperation());
        newCallExp.setType(callExp.getType());
        
        Map<String, List<Object>> res = new HashMap<String, List<Object>>();

        List<Object> left = new ArrayList<Object>();
        List<Object> right = new ArrayList<Object>();
        try {
            left.addAll(sourceResult.get(LEFT_SIDE_KEY));
        } catch (Exception e) {
        }

        try {
            right.addAll(sourceResult.get(RIGHT_SIDE_KEY));
        } catch (Exception e) {
            // TODO: handle exception
        }

        // for (Map<String, List<Object>> argResult : argumentResults)

        return res;
    }

    /**
     * Сортирует по различным коллекциям переменные и константы. Переменные в
     * список по ключу {@link MyOclVisitor#LEFT_SIDE_KEY}, константы - в список
     * по ключу {@link MyOclVisitor#LEFT_SIDE_KEY}.
     * 
     * @param sourceResult
     * @param argumentResults
     * @return
     */
    private Map<String, List<Object>> createStandartResult(
            OperationCallExp<C, O> callExp,
            Map<String, List<Object>> sourceResult,
            List<Map<String, List<Object>>> argumentResults) {

        Map<String, List<Object>> res = new HashMap<String, List<Object>>();
        List<Object> left = new ArrayList<Object>();
        try {
            left.addAll(sourceResult.get(LEFT_SIDE_KEY));
        } catch (Exception e) {
        }

        List<Object> right = new ArrayList<Object>();
        try {
            right.addAll(sourceResult.get(RIGHT_SIDE_KEY));
        } catch (Exception e) {
        }

        for (Map<String, List<Object>> argResult : argumentResults) {
            try {
                left.addAll(argResult.get(LEFT_SIDE_KEY));
            } catch (Exception e) {
            }

            try {
                right.addAll(argResult.get(RIGHT_SIDE_KEY));
            } catch (Exception e) {
            }
        }

        res.put(LEFT_SIDE_KEY, left);
        res.put(RIGHT_SIDE_KEY, right);
        return res;
    }
}
